/**
 * @author camilo AT Bronson ID .com
 * version 2.0b1
 * 
 * change log:
 *
 * version 2.0b1
 * Complete new version constructed to see events as functions with a type attribute.
 * Much better event handling with less code and less execution code (no more switch cases to define what should be triggered).
 * Also chnaged name from 'Facade' to 'Master'
 * Added new method called BasicMVC.addNamespace which is used to create namespaces
 *
 * version .53
 * better obeserver handling and error messages when registring
 * added a new method: unregisterAll() that cleans up all registered classes
 * 
 * version .52
 * better debug info structure
 * 
 * version .51
 * IE contructor fix
 * 
 * A small attempt at some basic MVC methods for javascript. Not complete. Not full MVC. Just a small step.
 * The inheritance class functions are inspired by John Resig who in turn was inspired by Base2 and Prototype
 * Thank you for the inspiration and tips guys and girls.
 */
var BasicMVC = {};

(function()
{
	var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

  	// The base Class implementation (does nothing)
  	this.BasicMVC.Class = function(){};

  	// Create a new Class that inherits from this class
  	BasicMVC.Class.extend = function(prop) 
	{
    	var _super = this.prototype;

	    // Instantiate a base class (but only create the instance,
	    // don't run the init constructor)
	    initializing = true;
	    var prototype = new this();
	    initializing = false;

		var hasConstructor = false;
		
	    // Copy the properties over onto the new prototype
	    for (var name in prop) {
	      // Check if we're overwriting an existing function
	      prototype[name] = typeof prop[name] == "function" &&
	        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
	        (function(name, fn){
	          return function() {
	            var tmp = this._super;

	            // Add a new ._super() method that is the same method
	            // but on the super-class
	            this._super = _super[name];

	            // The method only need to be bound temporarily, so we
	            // remove it when we're done executing
	            var ret = fn.apply(this, arguments);       
	            this._super = tmp;

	            return ret;
	          };
	        })(name, prop[name]) :
	        prop[name];
			
			if (name == "constructor") hasConstructor = true;
			
	    }
		
		// this is for IE. ie doesnt list "constructor" as part of the key/value object "prop"	
		if (!hasConstructor) 
		{
			prototype["constructor"] = prop["constructor"];
		}
	
		prototype["localScope"] = function(fn)
		{
			var self = this;
			var args = arguments;
			
			return function()
			{
				var argsList = []
				for(var i = 0; i < arguments.length; i++)
				{
					argsList.push(arguments[i]);
				}
				if (args != null) 
				{
					for(var i = 0; i < args.length; i++)
					{
						argsList.push(args[i]);
					}
				}
				fn.apply(self, argsList);
			}
			
			delete self;
		}

	    // The dummy class constructor
	    BasicMVC.Class = function() 
		{
	      	// All construction is actually done in the init method
	      	if ( !initializing && this.constructor )
			{
				this.constructor.apply(this, arguments);
			}  
	  	}
		
	
	    // Populate our constructed prototype object
	    BasicMVC.Class.prototype = prototype;
	
		

	    // Enforce the constructor to be what we expect
	   	BasicMVC.Class.constructor = BasicMVC.Class;

	    // And make this class extendable
	    BasicMVC.Class.extend = arguments.callee;

	    return BasicMVC.Class;
  	};
}
)();

/**
 * Creates a namespace but levaes it aone if the namespace is already defined
 * @param namespaceString
 */
BasicMVC.addNamespace = function(namespaceString)
{
	var nameStringParts = namespaceString.split(".");
	var currentNamespace = window;
	for (var i = 0, ii = nameStringParts.length; i < ii; i++)
	{
		currentNamespace = currentNamespace[nameStringParts[i]] = currentNamespace[nameStringParts[i]] || {};
	}
}

/**
 * The maste object that is an observer
 */
BasicMVC.Master = function()
{
	this.objects = [];
	this.objectsRef = [];
	this.objectsFriendlyName = {};
	this.debug = false;
	this.debugShowData = false;
	this.referencesThroughEventName = {};
}

BasicMVC.Master.master;

/**
 * Always get the master instance through this
 */
BasicMVC.Master.getInstance = function()
{
	if (BasicMVC.Master.master == null)
	{
		BasicMVC.Master.master = new BasicMVC.Master();
	}
	return BasicMVC.Master.master;
}

/**
 * Register a class to the list of classes
 * @param {Object} object
 */
BasicMVC.Master.prototype.register = function(object)
{
	var undefined;

	if (this.debug)
	{
		//console.info("Register class: " + object.name);
		// check if the object beeing registered has a valid name
		if (object.name == null)
		{
			console.error("This class doesnt have the name properity set: ", object);
			throw "doesnt have a name attribute. this is needed in BasicMVC.";
		}
	}


	// create a unique name
	var uniqueName = object.name + "-" + Math.random().toString().replace(/\./, "");
	object.uniqueName = uniqueName;

	// set a reference with the unique name
	this.objectsRef[uniqueName] = object;

	// create an array with friendly names
	if (this.objectsFriendlyName[object.name] == null)
	{
		this.objectsFriendlyName[object.name] = [];
	}

	// save the object in an array with friendly names
	this.objectsFriendlyName[object.name].push(object);


	// go through all events for the ovject and make the subscription
	var handlerList = object.eventListeners();
	for (var i = 0; i < handlerList.length; i++)
	{
		var handlerListItem = handlerList[i];

		if (handlerListItem.event == undefined)
		{
			throw "Listener event has to befined as listener.event at " + object.name;
		}
		else if (handlerListItem.handler == undefined)
		{
			throw "Listener handler has to be defined at " + object.name;
		}

		var type = this.getEventType(handlerListItem.event)

		if (type == undefined)
		{
			console.error("ERROR: event name not defined in object '" + object.name + "' when registering to master.");
		}
		else
		{
			if (this.referencesThroughEventName[type] == undefined)
			{
				this.referencesThroughEventName[type] = [];
			}
			this.referencesThroughEventName[type].push({object: object, handler: handlerListItem.handler});
		}
	}
	this.objects.push(object);

}

BasicMVC.Master.prototype.getInstanceByName = function(name)
{
	var returnObject = null;

	if (typeof this.objectsFriendlyName[name] == "object")
	{
		if (this.objectsFriendlyName[name].length > 1)
		{
			returnObject = this.objectsFriendlyName[name];
		}
		else
		{
			returnObject = this.objectsFriendlyName[name][0];
		}
	}

	return returnObject;
}

BasicMVC.Master.prototype.unregisterAll = function()
{
	for (var eventName in this.referencesThroughEventName)
	{
		this.referencesThroughEventName[eventName] = null;
		delete this.referencesThroughEventName[eventName];
	}

	this.referencesThroughEventName = null;
	this.objects = null;
	this.objectsFriendlyName = null;

	delete this.referencesThroughEventName;
	delete this.objects;
	delete this.objectsFriendlyName;

}

BasicMVC.Master.prototype.getEventType = function(classRef)
{
	var tempClassInstance = new classRef();
	var type = tempClassInstance.type;
	delete tempClassInstance;
	return type;
}


/**
 * When an event is dispatched, we look trough the registered objects and call the ones that are listening
 * @param {Object} eventName
 */
BasicMVC.Master.prototype.sendEvent = function(event)
{

	if(this.debug)
	{
		if(this.debugShowData)
		{
			console.info("BasicMVC EVENT: " + event.type + " - ", event);
		}

		else
		{
			console.info("BasicMVC EVENT: " + event.type);
		}
	}

	// ok lets iterate through all objects that have a subscription
	var subscriptorList = this.referencesThroughEventName[event.type];
	
	if (subscriptorList != null)
	{
		for (var i = 0, ii = subscriptorList.length; i < ii; i++)
		{
			subscriptorList[i].handler.apply(subscriptorList[i].object, [event]);
		}
	}

}


/**
 * This is the class that other classes should inherit from (or be based from)
 */
BasicMVC.Base = BasicMVC.Class.extend({

	constructor: function()
	{

	},

	master: BasicMVC.Master.getInstance(),

	/**
	 * This function dispatches an event and passes the event identifier and the data forward to the BasicMVCMaster "singleton" (or just a global variable in js)
	 * thereby triggering the chain that calls everythign else
	 * @alias BasicMVC.Base.prototype.sendEvent
	 * @param {Object} eventString
	 */
	sendEvent: function(event)
	{
		var undefined;
		if (event.type == undefined)
		{
			console.log("ERROR: event name not defined when dispatched from '" + this.name + "'");
		}
		else
		{
			this.master.sendEvent.apply(this.master, [event]);
		}
	},

	/**
	 * @alias BasicMVC.Base.prototype.register
	 * @param {Object} name
	 */
	register: function(name)
	{
		this.register(this);
	},

	/**
	 * All classes inheriting from this one should have a function called listeners which returns an array
	 * @alias BasicMVC.Base.prototype.eventListeners
	 */
	eventListeners: function()
	{
		return [];
	}

});




